package sdk

import (
	"encoding/json"
	"fmt"
	"net/http"
	"runtime"
	"strings"

	"gitee.com/youkelike/ziam/zsdk/sdk/log"
	"gitee.com/youkelike/ziam/zsdk/sdk/request"
	"gitee.com/youkelike/ziam/zsdk/sdk/response"
	tlsutil "gitee.com/youkelike/ziam/zutils/tls"
)

var defaultUserAgent = fmt.Sprintf("MEDUSDKGo/%s (%s; %s) Golang/%s", Version, runtime.GOOS, runtime.GOARCH, strings.Trim(runtime.Version(), "go"))

type Client struct {
	Config *Config
	Logger log.Logger
}

func (c *Client) Init() *Client {
	c.Logger = log.New()
	return c
}

func (c *Client) SetConfig(config *Config) *Client {
	c.Config = config
	c.Logger.SetLevel(config.LogLevel)
	return c
}

func (c *Client) Send(req request.Request, resp response.Response) error {
	method := req.GetMethod()
	builder := GetParameterBuilder(method, c.Logger)
	// 序列化后传给 BuildURL 是为了拿到接口中保存的动态类型结构体，并去掉带有 omitempty 标签且为零值的字段
	jsonReq, _ := json.Marshal(req)
	// 构造 path、query 部分，并拼接到一起
	encodedUrl, err := builder.BuildURL(req.GetURL(), jsonReq)
	if err != nil {
		return err
	}

	// 构造完整的 url
	endPoint := c.Config.Endpoint
	if endPoint == "" {
		endPoint = fmt.Sprintf("%s/%s", defaultEndpoint, c.Config.ServiceName)
	}
	reqUrl := fmt.Sprintf("%s://%s/%s%s", c.Config.Scheme, endPoint, req.GetVersion(), encodedUrl)

	// 构造 body，会去掉定义在 BaseRequest 中的字段
	body, err := builder.BuildBody(jsonReq)
	if err != nil {
		return err
	}

	rawResp, err := c.doSend(method, reqUrl, body, req.GetHeaders())
	if err != nil {
		return err
	}

	return response.ParseFromHttpResponse(rawResp, resp)
}

type SignFunc func(*http.Request) error

func (c *Client) doSend(method, url, data string, header map[string]string) (*http.Response, error) {
	client := http.Client{Timeout: c.Config.Timeout}

	// TLS 配置
	tlsConfig, err := tlsutil.TLSConfigFor(&c.Config.TLSClientConfig)
	if err != nil {
		return nil, err
	}
	client.Transport = &http.Transport{TLSClientConfig: tlsConfig}

	req, err := http.NewRequest(method, url, strings.NewReader(data))
	if err != nil {
		c.Logger.Errorf("%s", err.Error())
		return nil, err
	}

	c.setHeader(req, header)

	// 加上 jwt header，它不会用到 body 参数
	if len(c.Config.BearerToken) > 0 {
		// 直接填上 jwt token，适合 apiserver 用
		req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.Config.BearerToken))
	} else {
		// 新生成一个 jwt token，适合 authz 用
		signer := NewSigner(c.Config.signMethod, c.Config.Credential, c.Logger)
		_ = signer.Sign(c.Config.ServiceName, req, strings.NewReader(data))
	}

	return client.Do(req)
}

func (c *Client) setHeader(req *http.Request, header map[string]string) {
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("User-Agent", defaultUserAgent)

	for k, v := range header {
		req.Header.Set(k, v)
	}

	for k, v := range req.Header {
		c.Logger.Infof("header key: %s, header value: %s", k, v)
	}
}
